package org.jb.coinche.server.business.transition;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.jb.coinche.client.business.message.PlayCardMessage;
import org.jb.coinche.client.business.model.Auction;
import org.jb.coinche.client.business.model.Card;
import org.jb.coinche.client.business.model.Player;
import org.jb.coinche.client.business.model.RoundTurn;
import org.jb.coinche.client.business.model.RoundTurnFinished;
import org.jb.coinche.client.user.UserLogin;
import org.jb.coinche.server.business.ColorComparator;
import org.jb.coinche.server.business.Constant;
import org.jb.coinche.server.statemachine.Message;
import org.jb.coinche.server.statemachine.Transition;

import com.google.inject.Inject;
import com.google.inject.Provider;

/*
 * 
 */
public class RoundTurnTransition implements Transition {

	@Inject @UserLogin
	private Provider<String> user;
	
	/*
	 * Un joueur joue
	 */
	@Message
	public Object processPlayCard( RoundTurn roundTurn, PlayCardMessage msg ) {
		
		String userName = user.get();
		
		Player p = roundTurn.getRound().getGame().getPlayer( userName );
		if (p == null)
			throw new IllegalArgumentException("Player not found");
		
		if (roundTurn.getPlayedCard(p) != null)
			throw new IllegalArgumentException("Player has already played");
		
		if (roundTurn.getCurrentPlayer() != p)
			throw new IllegalArgumentException("Player should not play now");
		
		if (!p.getCards().remove( msg.getCard() ))
			throw new IllegalArgumentException("Card not found");

		roundTurn.play( p, msg.getCard() );
		
		if ( roundTurn.isFinished() ) {
			/*
			 * Tout le monde a joué => On calcul qui a gagné
			 */
			Player winner = getWinner(roundTurn);
			
			roundTurn.getRound().addScore( winner.getTeam(), getScore( roundTurn.getRound().getAuction(), roundTurn.getPlayedCards() ) );
				
			RoundTurnFinished rtFinished = new RoundTurnFinished();
			rtFinished.setWinner( winner );
			rtFinished.setRound( roundTurn.getRound() );
			
			for( Player pc : roundTurn.getRound().getGame().getPlayers() ) {
				rtFinished.setPlayedCard( pc, roundTurn.getPlayedCard(pc) );
			}
			
			return rtFinished;
			
		} else {
			
			buildPlayableCard( roundTurn );
			
			return roundTurn;
			
		}
	}
	
	/*
	 * 
	 */
	public Player getWinner( RoundTurn rt ) {
		
		ColorComparator cp = new ColorComparator(rt.getRound().getAuction().getTrump(), rt.getAskedColor() );
		Player winner = null;
		Card winnerCard = null;
		
		for( Player p : rt.getGame().getPlayers() ) {
			
			Card playerCard = rt.getPlayedCard(p);
			
			if (winnerCard == null || cp.compare(playerCard, winnerCard) < 0) {
				winner = p;
				winnerCard = playerCard;
			}
		}
		
		return winner;
	}
	
	/**
	 * 
	 * @param auction
	 * @param cards
	 */
	public int getScore( Auction auction, Collection<Card> cards ) {
		int score = 0;
		
		for( Card card : cards ) {
			boolean isTrump = card.getColor().equals( auction.getTrump() );
			
			if (isTrump) {
				int index = Constant.trumpValues.indexOf( card.getValue() );
				
				score += Constant.trumpScore.get( index );
			} else {
				int index = Constant.defaultValues.indexOf( card.getValue() );
				
				score += Constant.defaultScore.get( index );
			}
		}
		
		return score;
	}
	
	/*
	 * 
	 */
	public void buildPlayableCard( RoundTurn rt ) {
		
		if (rt.getAskedColor() == null)
			return;
		
		ColorComparator cp = new ColorComparator(rt.getRound().getAuction().getTrump(), rt.getAskedColor() );
		
		Card higherTrumpCard = null;
		Player master = null;
		Card masterCard = null;
		
		/*
		 * Calcul des stats sur le tour en cours
		 */
		for( Player p : rt.getGame().getPlayers() ) {
			Card pc = rt.getPlayedCard(p);
			if (pc == null)
				continue;
			
			if (pc.getColor().equals(cp.getTrump())) {
				if (higherTrumpCard == null) {
					higherTrumpCard = pc;
				} else {
					if (cp.compare(higherTrumpCard, pc) > 0) {
						higherTrumpCard = pc;
					}
				}
			}
			
			if (master == null) {
				master = p;
				masterCard = pc;
			} else if (cp.compare(masterCard, pc) > 0) {
				master = p;
				masterCard = pc;
			}
		}
		
		/*
		 * Calcul des cartes possibles
		 */
		Map< Player, Collection<Card> > playableCards = new HashMap< Player, Collection<Card> >();
		for( Player p : rt.getGame().getPlayers() ) {
			if (rt.getPlayedCard(p) != null)
				continue;
			
			Collection<Card> cards = getPlayerPossibleCard(p, rt.getAskedColor(), cp, master, higherTrumpCard );
			if (cards != null && !cards.isEmpty()) {
				playableCards.put(p, cards);
			}
			
		}
		
		rt.setPlayableCards(playableCards);
	}
	
	/*
	 * 
	 */
	public List<Card> getPlayerPossibleCard( Player p, Card.Color askedColor, ColorComparator trumpComparator, Player master, Card higherTrumpCard ) {
		
		Card.Color trump = trumpComparator.getTrump();
		
		ArrayList<Card> cards = new ArrayList<Card>();

		/*
		 * 1/ Le joueur possede la couleur demandée
		 */
		if (playerHasColor( p, askedColor )) {
			/*
			 * Dans ce cas, il est obligé de jouer de cette couleur
			 */
			
			if (askedColor.equals( trump )) {
				List<Card> trumpCards = getPlayerPossibleTrumpCard(p, trumpComparator, higherTrumpCard);
				if (trumpCards == null || trumpCards.isEmpty()) {
					return null; // Pas d'atoux, on peut jouer n'importequoi
				}
				cards.addAll(trumpCards);
			} else {
				
				for( Card c : p.getCards() ) {
					if (c.getColor().equals( askedColor )) {
						cards.add(c);
					}
				}
				
				return cards;
			}
		} 
		/*
		 * 2/ Le joueur n'a pas la couleur demandé
		 */
		else {
			
			/*
			 * SI le partenaire est maitre, alors il est possible de 'pisser'
			 * n'importequelle carte non atoux
			 */
			if (p.getTeam().equals( master.getTeam() )) {
				for( Card c : p.getCards() ) {
					if (!c.getColor().equals( trump )) {
						cards.add(c);
					}
				}
			}
			
			if (playerHasColor(p, trump )) {
				/*
				 * On peut joueur atoux
				 */
				cards.addAll( getPlayerPossibleTrumpCard(p, trumpComparator, higherTrumpCard) );
			}
		}
		
		return cards;
	}
	
	/*
	 * 
	 */
	public List<Card> getPlayerPossibleTrumpCard( Player p, ColorComparator trumpComparator, Card higherTrumpCard ) {
		ArrayList<Card> cards = new ArrayList<Card>();
		
		/*
		 * Il faut forcement monter en atoux
		 */
		List<Card> trumpCard = getPlayerHigherTrump( p, higherTrumpCard, trumpComparator );
		
		if (trumpCard != null) {
			
			cards.addAll( trumpCard );
		
		} else {
			/*
			 * On n'a pas d'atoux plus elevé => on peut joueur n'importequoi
			 */
			for( Card c : p.getCards() ) {
				if (c.getColor().equals( trumpComparator.getTrump() )) {
					cards.add(c);
				}
			}
		}
		
		return cards;
	}
	
	/*
	 * 
	 */
	private List<Card> getPlayerHigherTrump(Player p, Card higherTrumpCard, ColorComparator trumpComparator) {
		
		if (higherTrumpCard == null)
			return null;
		
		ArrayList<Card> cards = null;
		
		for( Card c : p.getCards() ) {
			if (c.getColor().equals( higherTrumpCard.getColor() ) &&
				trumpComparator.compare(c, higherTrumpCard) < 0) {
				
				if (cards == null) {
					cards = new ArrayList<Card>();
				}
				
				cards.add(c);
			}
		}
		
		return cards;
	}

	/**
	 * 
	 * @param p
	 * @param color
	 * @return
	 */
	public boolean playerHasColor( Player p, Card.Color color ) {
		for( Card c : p.getCards()) {
			if (c.getColor().equals(color))
				return true;
		}
		
		return false;
	}
}
